﻿using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using UCS.XNA.Draughts.AI;
using UCS.XNA.Draughts.Boards;
using UCS.XNA.Draughts.Pieces;
using UCS.XNA.Draughts.Squares;

namespace UCS.XNA.Draughts.People
{
    public class Referee
    {
        #region Attributes and Properties

        private Board _board;
        public Board Board
        {
            get { return _board; }
            set { _board = value; }
        }

        private Player _currentPlayer;
        public Player CurrentPlayer
        {
            get { return _currentPlayer; }
            set { _currentPlayer = value; }
        }

        private Player _winner;
        public Player Winner
        {
            get { return _winner; }
        }

        #endregion

        #region Private Members

        private ComputerMoveCalculation _pendingCalculation = null;
        private PieceMovement _lastMovement = null;
        private Piece _lastPieceMoved;
        private List<Square> _validSquares = new List<Square>();
        private List<Square> _invalidSquares = new List<Square>();

        #endregion

        #region Constructors

        public Referee(Board board)
        {
            _currentPlayer = Players.WhitePlayer;
            _board = board;
        }

        #endregion

        #region Public Methods

        public void AssignEvents()
        {
            Players.WhitePlayer.PickingPieceUp += this.currentPlayer_PickingPieceUp;
            Players.WhitePlayer.PuttingPieceDown += this.currentPlayer_PuttingPieceDown;
            Players.WhitePlayer.PieceMoved += this.currentPlayer_PieceMoved;
            Players.WhitePlayer.PieceMovementCompleted += currentPlayer_PieceMovementCompleted;

            Players.BlackPlayer.PickingPieceUp += this.currentPlayer_PickingPieceUp;
            Players.BlackPlayer.PuttingPieceDown += this.currentPlayer_PuttingPieceDown;
            Players.BlackPlayer.PieceMoved += this.currentPlayer_PieceMoved;
            Players.BlackPlayer.PieceMovementCompleted += currentPlayer_PieceMovementCompleted;
        }

        public void Update(GameTime gameTime)
        {
            _currentPlayer.Update(gameTime);
        }

        public Referee Clone(Board board)
        {
            Referee clone = new Referee(board);
            clone.CurrentPlayer = _currentPlayer;

            return clone;
        }

        #endregion

        #region Private Methods

        private void CheckComputerMoveCalculation()
        {
            //if (_currentPlayer.IsComputerControlled && _winner == null)
            //{
            //    if (_pendingCalculation == null)
            //    {
            //        Board board = _board.Clone();
            //        _pendingCalculation = new ComputerMoveCalculation(new BoardState());
            //        _pendingCalculation.Start();
            //    }

            //    if (_pendingCalculation.IsFinished)
            //    {
            //        //BoardState nextState = _pendingCalculation.Result;
            //        //_board.ApplyMovement(_board[nextState.Piece.BoardPosition].Piece, _board[nextState.Piece.BoardPosition], _board[nextState.Piece.BoardPosition + nextState.Movement.BoardMovement], _currentPlayer);
            //        //_pendingCalculation = null;
            //    }
            //}
        }

        private List<PieceMovement> GetPlayerMovements(Player player)
        {
            List<PieceMovement> movements = new List<PieceMovement>();

            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    Piece squarePiece = _board.Squares[i, j].Piece;

                    if (squarePiece != null && squarePiece.Color == player.Color)
                        movements.AddRange(GetMovements(squarePiece));
                }
            }

            return movements;
        }

        private List<PieceMovement> GetMovements(Piece piece)
        {
            List<PieceMovement> validMovements = new List<PieceMovement>();

            foreach (var movement in piece.Movements)
            {
                Square movementSquare = _board[piece.BoardPosition + movement.BoardMovement];

                if (movementSquare != null)
                {
                    if (movementSquare.Piece == null)
                    {
                        if (movement.JumpPosition != null)
                        {
                            Square jumpSquare = _board[piece.BoardPosition + movement.JumpPosition];
                            if (jumpSquare != null && jumpSquare.Piece != null && jumpSquare.Piece.Color != piece.Color)
                                validMovements.Add(movement);
                        }
                        else
                            validMovements.Add(movement);
                    }
                }
            }

            return validMovements;
        }

        private void ChangePlayerIfNeeded()
        {
            if (this._lastPieceMoved == null)
            {
                this.ChangePlayer();
                return;
            }

            var validMovements = this.GetMovements(this._lastPieceMoved);
            var isJumpMovement = validMovements.Any(m => m.JumpPosition != null);

            if (isJumpMovement && _lastMovement.JumpPosition != null)
                return;

            this.ChangePlayer();
        }

        private bool NeedsToChangeCurrentPlayer()
        {
            return false;
        }

        private bool CheckIfPlayerHasQueenJumpMovements()
        {
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    Piece squarePiece = _board.Squares[i, j].Piece;

                    if (squarePiece != null &&
                        squarePiece.Color == _currentPlayer.Color &&
                        squarePiece is QueenPiece &&
                        GetMovements(squarePiece).Count(m => m.JumpPosition != null) > 0)
                        return true;
                }
            }

            return false;
        }

        private void CheckPlayerVictory()
        {
            bool foundWhite = false;
            bool foundBlack = false;

            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    if (_board.Squares[i, j].Piece != null)
                        if (_board.Squares[i, j].Piece.Color == PieceColor.Black)
                            foundBlack = true;
                        else
                            foundWhite = true;
                }
            }

            if (!foundWhite)
            {
                _winner = Players.BlackPlayer;
                return;
            }
            else if (!foundBlack)
            {
                _winner = Players.WhitePlayer;
                return;
            }

            if (_currentPlayer == Players.WhitePlayer)
            {
                List<PieceMovement> whiteMovements = GetPlayerMovements(Players.WhitePlayer);

                if (whiteMovements.Count == 0)
                    _winner = Players.BlackPlayer;
            }
            else
            {
                List<PieceMovement> blackMovements = GetPlayerMovements(Players.BlackPlayer);

                if (blackMovements.Count == 0)
                    _winner = Players.WhitePlayer;
            }
        }

        private void ChangePlayer()
        {
            if (this._currentPlayer == Players.WhitePlayer)
                this._currentPlayer = Players.BlackPlayer;
            else
                this._currentPlayer = Players.WhitePlayer;

            _lastMovement = null;
            _lastPieceMoved = null;
        }

        private void ValidateSquares(Piece selectedPiece)
        {
            var movements = this.GetValidMovements(selectedPiece);
            foreach (var movement in movements)
            {
                var position = selectedPiece.BoardPosition + movement.BoardMovement;
                var square = _board[position];

                square.State = SquareState.Valid;
                _validSquares.Add(square);
            }
        }

        private List<PieceMovement> GetValidMovements(Piece piece)
        {
            List<PieceMovement> movements = this.GetMovements(piece);
            List<PieceMovement> validMovements = new List<PieceMovement>();

            foreach (var movement in movements)
            {
                if (this.GetValidMovement(piece, _board[piece.BoardPosition], _board[piece.BoardPosition + movement.BoardMovement]) != null)
                    validMovements.Add(movement);
            }

            return validMovements;
        }

        private PieceMovement GetValidMovement(Piece piece, Square oldSquare, Square newSquare)
        {
            if (!newSquare.IsEmpty)
                return null;

            List<PieceMovement> pieceValidMovements = GetMovements(piece);

            PieceMovement newMovement = pieceValidMovements.FirstOrDefault(m =>
            {
                var resultPosition = oldSquare.BoardPosition + m.BoardMovement;
                return resultPosition.Equals(newSquare.BoardPosition);
            });

            if (newMovement == null)
                return null;

            if (_lastMovement != null && _lastMovement.IsJump)
                return piece == _lastPieceMoved && newMovement.IsJump ? newMovement : null;

            if (newMovement.IsJump)
                return newMovement;

            List<PieceMovement> allPiecesValidMovements = this.GetPlayerMovements(_currentPlayer);

            return allPiecesValidMovements.FirstOrDefault(m => m.IsJump) == null ? newMovement : null;
        }

        private void ResetValidSquares()
        {
            foreach (var validSquare in _validSquares)
                validSquare.State = SquareState.Normal;
            _validSquares.Clear();
        }

        private void ResetInvalidSquares()
        {
            foreach (var invalidSquare in _invalidSquares)
                invalidSquare.State = SquareState.Normal;
            _invalidSquares.Clear();
        }

        private void TransformQueens()
        {
            for (int j = 1; j < 8; j += 2)
            {
                var square = _board.Squares[0, j];
                var piece = square.Piece;

                if (piece != null && piece.Color == PieceColor.White)
                {
                    square.Piece = new QueenPiece(piece.Color, piece.BoardPosition);
                    square.Piece.InitializeTextures();
                }
            }

            for (int j = 0; j < 8; j += 2)
            {
                var square = _board.Squares[7, j];
                var piece = square.Piece;

                if (piece != null && piece.Color == PieceColor.Black)
                {
                    square.Piece = new QueenPiece(piece.Color, piece.BoardPosition);
                    square.Piece.InitializeTextures();
                }
            }
        }

        #endregion

        #region Signed Events Methods

        private void currentPlayer_PickingPieceUp(object sender, CancelEventArgs e)
        {
            Piece piece = sender as Piece;
            if (piece.Color == _currentPlayer.Color)
                this.ValidateSquares(piece);
            else
                e.Cancel = true;
        }

        private void currentPlayer_PuttingPieceDown(object sender, PuttingPieceDownEventArgs e)
        {
            if (e.Position.I < 8 && e.Position.J < 8 && e.Position.I >= 0 && e.Position.J >= 0)
            {
                var hoveringSquare = _board[e.Position];

                if (hoveringSquare.State != SquareState.Valid)
                    e.Cancel = true;
            }

            this.ResetValidSquares();
            this.ResetInvalidSquares();
        }

        private void currentPlayer_PieceMoved(object sender, PieceMovedEventArgs e)
        {
            this.ResetInvalidSquares();

            if (e.NewPosition.I < 8 && e.NewPosition.J < 8 && e.NewPosition.I >= 0 && e.NewPosition.J >= 0)
            {
                var hoveringSquare = _board.Squares[e.NewPosition.I, e.NewPosition.J];

                if (hoveringSquare.State == SquareState.Normal)
                {
                    hoveringSquare.State = SquareState.Invalid;
                    _invalidSquares.Add(hoveringSquare);
                }
            }
        }

        private void currentPlayer_PieceMovementCompleted(object sender, PieceMovementCompletedEventArgs e)
        {
            _lastMovement = e.Movement;
            _lastPieceMoved = e.Piece;
            this.CheckComputerMoveCalculation();
            this.ChangePlayerIfNeeded();
        }

        #endregion
    }
}